# -*- coding: utf-8 -*-
#
# Copyright 2009 Vanderbilt University
# 
# Licensed under the Apache License, Version 2.0 (the "License"); 
# you may not use this file except in compliance with the License. 
# You may obtain a copy of the License at 
# 
#     http://www.apache.org/licenses/LICENSE-2.0 
# 
# Unless required by applicable law or agreed to in writing, software 
# distributed under the License is distributed on an "AS IS" BASIS, 
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
# See the License for the specific language governing permissions and 
# limitations under the License. 

"""Important core classes for the network. Due to high usage, this module
can be compiled into machine code with Cython for improved performance.

.. moduleauthor:: John Paulett <john.paulett -at- vanderbilt.edu>
"""

class EdgeDetail(object):
    """Structure that holds details about an edge, such as the similarity and 
    the size. EdgeDetail objects are not hashable, thus not suitable to be used
    as keys.
    
    Attributes:
    
        * *similarity*
        * *size* The number of items in common between the two nodes.
        * *joint* The joint probability of node A and node B, P(A,B) or
          P(A and B). Also known as the support of a rule.
        * *conditional* The conditional probability of node B give node A, 
          P(B|A). Also known as the confidence of a rule.
    
    These attributes default to 0.  They can optionally be set as keyed 
    arguments to the constructor:
    
    >>> d = EdgeDetail(size=450, joint=0.3, conditional=0.7)
    """
    def __init__(self, **kw):
        self.similarity = 0
        self.size = 0
        self.joint = 0
        self.conditional = 0
        
        if kw is not None:
            for k, v in kw.iteritems():
                self.__setattr__(k, v) 
    
    def items(self):
        """Returns a list of tuples for the attributes of the EdgeDetail. 
        """
        return self.__dict__.items()
    
    def __repr__(self):
        return '<EdgeDetail(size=%i)>' % self.size
    
    def __eq__(self, other):
        try:
            return self.__dict__ == other.__dict__
        except:
            return False
    
    def __ne__(self, other):
        return not self.__eq__(other)
    

class Node(object):
    """Class that represents a node in a graph.
    
    >>> Node('abc')
    <Node('abc', size=0)>
    """
    # TODO: conside using slots to save memory
    # http://pyref.infogami.com/__slots__
    #__slots__ = ('id', 'size')
    
    def __init__(self, id, size=0):
        self.id = id
        self.size = size
        
    def __eq__(self, other):
        """Override to base object on the id attribute.
        
        >>> a,b = Node(12), Node(12)
        >>> a == b
        True
        """
        if other is None:
            return False
        return self.id == other.id
    
    def __ne__(self, other):
        """
        >>> a,b = Node(1), Node(2)
        >>> a != b
        True
        """
        return not self.__eq__(other)
    
    def __hash__(self):
        """Override to base object on the id attribute.
        
        >>> string = 'a string'
        >>> node = Node('a string')
        >>> hash(string) == hash(node)
        True
        """
        return self.id.__hash__()
    
    def __str__(self):
        return str(self.id)
    
    def __repr__(self):
        # this forces all ids into a string, which it may not be
        return "<Node('%s', size=%i)>" % (self.id, self.size)
